package com.one.square.oauth.controller;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.one.square.common.redis.util.RedisUtils;
import com.one.square.core.constant.SecurityConstants;
import com.one.square.core.domain.Result;
import com.one.square.core.utils.JwtUtils;
import com.one.square.core.utils.RequestUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import java.security.Principal;
import java.util.Map;

/**
 * @author cwm
 * @Description 认证中心
 * /oauth/authorize：授权端点
 * /oauth/token：令牌端点
 * /oauth/confirm_access：用户确认授权提交端点
 * /oauth/error：授权服务错误信息端点
 * /oauth/check_token：用于资源服务访问的令牌解析端点
 * /oauth/token_key：提供公有密匙的端点，如果使用JWT令牌的话
 * @date 2021/10/25 下午4:24
 * @Version 1.0
 */
@Api(tags = "认证中心")
@RestController
@RequestMapping("/oauth")
@AllArgsConstructor
@Slf4j
public class AuthController {
    private TokenEndpoint tokenEndpoint;
    private RedisUtils redisUtils;

    /**
     * 重写oauth2.0登录暴露端点
     *
     * @param principal
     * @param parameters
     * @return
     * @throws HttpRequestMethodNotSupportedException
     */
    @ApiOperation(value = "OAuth2认证", notes = "登录入口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "grant_type", defaultValue = "password", value = "授权模式", required = true),
            @ApiImplicitParam(name = "client_id", defaultValue = "client", value = "Oauth2客户端ID", required = true),
            @ApiImplicitParam(name = "client_secret", defaultValue = "123456", value = "Oauth2客户端秘钥", required = true),
            @ApiImplicitParam(name = "refresh_token", value = "刷新token"),
            @ApiImplicitParam(name = "username", defaultValue = "admin", value = "用户名"),
            @ApiImplicitParam(name = "password", defaultValue = "123456", value = "用户密码")
    })
    @PostMapping("/token")
    public Object postAccessToken(
            @ApiIgnore Principal principal,
            @ApiIgnore @RequestParam Map<String, String> parameters
    ) throws HttpRequestMethodNotSupportedException {

        /**
         * 获取登录认证的客户端ID
         *
         * 兼容两种方式获取Oauth2客户端信息（client_id、client_secret）
         * 方式一：client_id、client_secret放在请求路径中(注：当前版本已废弃)
         * 方式二：放在请求头（Request Headers）中的Authorization字段，且经过加密，例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret
         */
        String clientId = RequestUtils.getOAuth2ClientId();
        log.info("OAuth认证授权 客户端ID:{}，请求参数：{}", clientId, JSONUtil.toJsonStr(parameters));
        OAuth2AccessToken accessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
        return Result.succeed(accessToken,"认证成功");
    }

    @ApiOperation(value = "注销")
    @DeleteMapping("/logout")
    public Result logout() {

        JSONObject payload = JwtUtils.getJwtPayload();
        // JWT唯一标识
        String jti = payload.getStr(SecurityConstants.JWT_JTI);
        // JWT过期时间戳(单位：秒)
        Long expireTime = payload.getLong(SecurityConstants.JWT_EXP);
        if (expireTime != null) {
            // 当前时间（单位：秒）
            long currentTime = System.currentTimeMillis() / 1000;
            if (expireTime > currentTime) {
                // token未过期，添加至缓存作为黑名单限制访问，缓存时间为token过期剩余时间
                redisUtils.set(SecurityConstants.TOKEN_BLACKLIST_PREFIX + jti, null, (expireTime - currentTime));
            }
        } else {
            // token 永不过期则永久加入黑名单
            redisUtils.set(SecurityConstants.TOKEN_BLACKLIST_PREFIX + jti, null);
        }
        return Result.succeed("注销成功");
    }
}
